package com.lwq.imserver.filter;

import com.auth0.jwt.interfaces.Claim;
import com.lwq.imserver.exception.AuthenticationException;
import com.lwq.imserver.model.system.SysUser;
import com.lwq.imserver.util.RedisUtil;
import com.lwq.imserver.util.TokenUtil;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Description:
 * Author: LiuWenQing
 * Datetime: 2022/1/28 11:29
 */
@Component
public class TokenFilter implements HandlerInterceptor {

    public TokenFilter() {
        super();
    }

    @Value("${permission.administratorAccounts}")
    private String adminAccount;
    @Autowired
    private RedisUtil redis;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Value("${token.expire}")
    private int tokenExpire;
    @Value("${token.header}")
    private String tokenHeader;

    @Order(value = 2)
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        /**
         * 过滤option请求
         */
        if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
            response.setHeader("Access-control-Allow-Origin" , request.getHeader("Origin"));
            response.setHeader("Access-Control-Allow-Methods" , "*");
            response.setHeader("Access-Control-Allow-Headers" ,
                    request.getHeader("Access-Control-Request-Headers"));
            response.setStatus(HttpStatus.OK.value());
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
        if (!(handler instanceof HandlerMethod)) {
            setCorsMappings(request, response);
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
        String token = request.getHeader(tokenHeader);
        if (StringUtils.isEmpty(token)) {
            setCorsMappings(request, response);
            throw new AuthenticationException("令牌错误,请登录");
        }

        token = TokenUtil.cutTokenBearer(token);
        Map<String, Claim> map;
        try {
            map = TokenUtil.verifyToken(token);
        } catch (Exception e) {
            setCorsMappings(request, response);
            throw new AuthenticationException("令牌过期,请重新登录");
        }
        String userId = map.get("userId").asString();
        if (StringUtils.isEmpty(redis.get(userId.toString()))) {
            setCorsMappings(request, response);
            throw new AuthenticationException("令牌过期,请重新登录");
        }
        SysUser user = mongoTemplate.findById(userId, SysUser.class);
        Map<String, String> valueMap = new HashMap<>();
        valueMap.put("token" , token);
        if (user == null) {
            setCorsMappings(request, response);
            throw new AuthenticationException("令牌异常,请重新登录");
        } else if (!StringUtils.isEmpty(adminAccount) &&
                Arrays.asList(adminAccount.split(",")).contains(user.getName())) {
            if (!user.getMutiLogin()) {
                String redisToken = redis.get(userId);
                if (!token.equals(redisToken)) {
                    throw new AuthenticationException("令牌过期,请重新登录");
                }
            }
            // 验证登录账号是否配置为管理员账号
            refreshToken(userId, token);
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }else{
            if (!user.getMutiLogin()) {
                String redisToken = redis.get(userId);
                if (!token.equals(redisToken)) {
                    throw new AuthenticationException("令牌过期,请重新登录");
                }
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    /**
     * 刷新token时间 续费
     *
     * @param userId
     */
    private void refreshToken(String userId, String token) {
        redis.put(userId, token, tokenExpire, TimeUnit.HOURS);
    }

    private void setCorsMappings(HttpServletRequest request, HttpServletResponse response) {
        String origin = request.getHeader("Origin");
        response.setHeader("Access-Control-Allow-Origin" , origin);
        response.setHeader("Access-Control-Allow-Methods" , "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age" , "3600");
        response.setHeader("Access-Control-Allow-Headers" , "x-requested-with,Authorization");
        response.setHeader("Access-Control-Allow-Credentials" , "true");
    }
}
